package com.ysj.aipicturebe.manager.upload;


import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpStatus;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;

import com.ysj.aipicturebe.exception.BusinessException;
import com.ysj.aipicturebe.exception.ErrorCode;
import com.ysj.aipicturebe.exception.ThrowUtils;
import org.springframework.stereotype.Service;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

/**
 * URL 图片上传
 */
@Service
public class UrlPictureUpload extends PictureUploadTemplate {
    /**
     * 该方法是重写的 `validPicture`，用于验证输入的图片 URL 是否符合要求。实现步骤如下：
     * 1. 将 `inputSource` 强制转换为字符串类型，得到图片的 URL 地址。
     * 2. 校验 URL 是否为空，如果为空则抛出参数错误异常。
     * 3. 使用 `URL` 类尝试解析 URL，如果格式不正确则抛出异常。
     * 4. 校验 URL 的协议是否为 HTTP 或 HTTPS，如果不是则抛出参数错误异常。
     * 5. 发送 HEAD 请求到 URL，检查文件是否存在。
     *
     * 6. 如果文件存在，获取响应头中的 `Content-Type` 和 `Content-Length`，进行以下校验：
     *   - 如果 `Content-Type` 不为空，校验其是否为允许的图片类型（如 JPEG、PNG、WEBP 等），如果不符合则抛出参数错误异常。
     *   - 如果 `Content-Length` 不为空，尝试将其转换为长整型，并校验文件大小是否超过 2MB，如果超过则抛出参数错误异常。
     * 7. 如果以上校验都通过，则认为输入的图片 URL 是有效的，可以进行后续处理。
     * @param inputSource
     */
    @Override
    protected void validPicture(Object inputSource) {
        String fileUrl = (String) inputSource;
        // 1. 校验非空
        ThrowUtils.throwIf(StrUtil.isBlank(fileUrl), ErrorCode.PARAMS_ERROR, "文件地址为空");

        // 2. 校验 URL 格式
        try {
            new URL(fileUrl);
        } catch (MalformedURLException e) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件地址格式不正确");
        }
        // 3. 校验 URL 的协议
        ThrowUtils.throwIf(!fileUrl.startsWith("http://") && !fileUrl.startsWith("https://"),
                ErrorCode.PARAMS_ERROR, "仅支持 HTTP 或 HTTPS 协议的文件地址"
        );
        // 4. 发送 HEAD 请求验证文件是否存在
        HttpResponse httpResponse = null;
        try {
            httpResponse = HttpUtil.createRequest(Method.HEAD, fileUrl)
                    .execute();
            // 未正常返回，无需执行其他判断
            if (httpResponse.getStatus() != HttpStatus.HTTP_OK) {
                return;
            }
            // 5. 文件存在，文件类型校验
            String contentType = httpResponse.header("Content-Type");
            // 不为空，才校验是否合法，这样校验规则相对宽松
            if (StrUtil.isNotBlank(contentType)) {
                // 允许的图片类型
                final List<String> ALLOW_CONTENT_TYPES = Arrays.asList("image/jpeg", "image/jpg", "image/png", "image/webp");
                ThrowUtils.throwIf(!ALLOW_CONTENT_TYPES.contains(contentType.toLowerCase()),
                        ErrorCode.PARAMS_ERROR, "文件类型错误");
            }
            // 6. 文件存在，文件大小校验
            String contentLengthStr = httpResponse.header("Content-Length");
            if (StrUtil.isNotBlank(contentLengthStr)) {
                try {
                    long contentLength = Long.parseLong(contentLengthStr);
                    final long ONE_M = 1024 * 1024;
                    ThrowUtils.throwIf(contentLength > 2 * ONE_M, ErrorCode.PARAMS_ERROR, "文件大小不能超过 2MB");
                } catch (NumberFormatException e) {
                    throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小格式异常");
                }
            }
        } finally {
            // 记得释放资源
            if (httpResponse != null) {
                httpResponse.close();
            }
        }
    }

    /**
     * 该方法是重写的 `getOriginFilename`，用于从输入的图片 URL 中提取原始文件名。实现步骤如下：
     *
     * 1. 将 `inputSource` 强制转换为字符串类型，得到图片的 URL 地址。
     * 2. 使用 Hutool 工具类 `FileUtil.mainName(fileUrl)`，从 URL 字符串中提取主文件名（不包含扩展名）。
     *
     * 例如，输入 `https://example.com/image/test.png`，返回结果为 `test`。此方法主要用于后续保存或处理文件时，获取文件的基础名称。
     * @param inputSource
     * @return
     */
    @Override
    protected String getOriginFilename(Object inputSource) {
        String fileUrl = (String) inputSource;
        return FileUtil.mainName(fileUrl);
    }

    /**
     * 该方法是重写的 `processFile`，用于处理输入源并生成本地临时文件。实现步骤如下：
     * 1. 将 `inputSource` 强制转换为字符串类型，得到图片的 URL 地址。
     * 2. 使用 Hutool 工具类 `HttpUtil.downloadFile(fileUrl, file)`，将指定的 URL 地址的文件下载到本地临时目录中。
     * 3. 如果下载过程中发生异常，将抛出异常，表示处理文件失败。
     * * 该方法主要用于将远程图片 URL 下载到本地，以便后续上传到对象存储或进行其他处理。
     * @param inputSource
     * @param file
     * @throws Exception
     */
    @Override
    protected void processFile(Object inputSource, File file) throws Exception {
        String fileUrl = (String) inputSource;
        // 下载文件到临时目录
        HttpUtil.downloadFile(fileUrl, file);
    }
}
